package internal

import (
	"embed"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/barasher/go-exiftool"
	"github.com/charmbracelet/lipgloss"
	"github.com/pelletier/go-toml/v2"
	variable "github.com/yorukot/superfile/src/config"
	"github.com/yorukot/superfile/src/config/icon"
)

// initialConfig load and handle all configuration files (spf config,hotkeys
// themes) setted up. Returns absolute path of dir pointing to the file Panel
func initialConfig(dir string) (toggleDotFileBool bool, toggleFooter bool, firstFilePanelDir string) {
	var err error

	// Open log stream
	logOutput, err = os.OpenFile(variable.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("打开日志文件时出错: %v", err)
	}

	loadConfigFile()

	loadHotkeysFile()

	loadThemeFile()

	icon.InitIcon(Config.Nerdfont)

	toggleDotFileData, err := os.ReadFile(variable.ToggleDotFile)
	if err != nil {
		outPutLog("读取 toggleDotFile 数据时出错:", err)
	}
	if string(toggleDotFileData) == "true" {
		toggleDotFileBool = true
	} else if string(toggleDotFileData) == "false" {
		toggleDotFileBool = false
	} else {
		toggleDotFileBool = false
	}

	toggleFooterData, err := os.ReadFile(variable.ToggleFooter)
	if err != nil {
		outPutLog("读取 toggleFooter 数据时出错:", err)
	}
	if string(toggleFooterData) == "true" {
		toggleFooter = true
	} else if string(toggleFooterData) == "false" {
		toggleFooter = false
	} else {
		toggleFooter = true
	}

	LoadThemeConfig()

	if Config.Metadata {
		et, err = exiftool.NewExiftool()
		if err != nil {
			outPutLog("初始化 exiftool 时出错", err)
		}
	}

	if dir != "" {
		firstFilePanelDir, err = filepath.Abs(dir)
	} else {
		Config.DefaultDirectory = strings.Replace(Config.DefaultDirectory, "~", variable.HomeDir, -1)
		firstFilePanelDir, err = filepath.Abs(Config.DefaultDirectory)
	}

	if err != nil {
		firstFilePanelDir = variable.HomeDir
	}

	return toggleDotFileBool, toggleFooter, firstFilePanelDir
}

// Load configurations from the configuration file. Compares the content
// with the default values and modify the config file to include default configs
// if the FixConfigFile flag is on
func loadConfigFile() {

	//Initialize default configs
	_ = toml.Unmarshal([]byte(ConfigTomlString), &Config)
	//Initialize empty configs
	tempForCheckMissingConfig := ConfigType{}

	data, err := os.ReadFile(variable.ConfigFile)
	if err != nil {
		log.Fatalf("Config file doesn't exist: %v", err)
	}

	// Insert data present in the config file inside temp variable
	_ = toml.Unmarshal(data, &tempForCheckMissingConfig)
	// Replace default values for values specifieds in config file
	err = toml.Unmarshal(data, &Config)
	if err != nil && !variable.FixConfigFile {
		fmt.Print(lipgloss.NewStyle().Foreground(lipgloss.Color("#F93939")).Render("错误") +
			lipgloss.NewStyle().Foreground(lipgloss.Color("#00FFEE")).Render(" ┃ ") +
			"解析配置文件出错\n")
		fmt.Println("要自动添加缺失的配置项，请使用 --fix-config-file 参数运行 Superfile：`spf --fix-config-file`")
	}

	// If data is different and FixConfigFile option is on, then fullfill then
	// fullfill the config file with the default values
	if !reflect.DeepEqual(Config, tempForCheckMissingConfig) && variable.FixConfigFile {
		tomlData, err := toml.Marshal(Config)
		if err != nil {
			log.Fatalf("编码配置时出错: %v", err)
		}

		err = os.WriteFile(variable.ConfigFile, tomlData, 0644)
		if err != nil {
			log.Fatalf("写入配置文件时出错: %v", err)
		}
	}

	if (Config.FilePreviewWidth > 10 || Config.FilePreviewWidth < 2) && Config.FilePreviewWidth != 0 {
		fmt.Println(loadConfigError("file_preview_width"))
		os.Exit(0)
	}

	if Config.SidebarWidth != 0 && (Config.SidebarWidth < 3 || Config.SidebarWidth > 20) {
		fmt.Println(loadConfigError("sidebar_width"))
		os.Exit(0)
	}
}

// Load keybinds from the hotkeys file. Compares the content
// with the default values and modify the hotkeys  if the FixHotkeys flag is on.
// If is off check if all hotkeys are properly setted
func loadHotkeysFile() {

	// load default Hotkeys configs
	_ = toml.Unmarshal([]byte(HotkeysTomlString), &hotkeys)
	hotkeysFromConfig := HotkeysType{}
	data, err := os.ReadFile(variable.HotkeysFile)

	if err != nil {
		log.Fatalf("Config file doesn't exist: %v", err)
	}
	// Load data from hotkeys file
	_ = toml.Unmarshal(data, &hotkeysFromConfig)
	// Override default hotkeys with the ones from the file
	err = toml.Unmarshal(data, &hotkeys)
	if err != nil {
		log.Fatalf("解析热键文件出错（配置文件可能有误）: %v", err)
	}

	hasMissingHotkeysInConfig := !reflect.DeepEqual(hotkeys, hotkeysFromConfig)

	// If FixHotKeys is not on then check if every needed hotkey is properly setted
	if hasMissingHotkeysInConfig && !variable.FixHotkeys {
		hotKeysConfig := reflect.ValueOf(hotkeysFromConfig)
		for i := 0; i < hotKeysConfig.NumField(); i++ {
			field := hotKeysConfig.Type().Field(i)
			value := hotKeysConfig.Field(i)
			name := field.Name
			isMissing := value.Len() == 0

			if isMissing {
				fmt.Print(lipgloss.NewStyle().Foreground(lipgloss.Color("#F93939")).Render("错误") +
					lipgloss.NewStyle().Foreground(lipgloss.Color("#00FFEE")).Render(" ┃ ") +
					fmt.Sprintf("热键配置中缺少字段 \"%s\"\n", name))
			}
		}
		fmt.Println("要自动添加缺失的热键，请使用 --fix-hotkeys 参数运行 Superfile：`spf --fix-hotkeys`")
	}

	// Override hotkey files with default configs if the Fix flag is on
	if hasMissingHotkeysInConfig && variable.FixHotkeys {
		writeHotkeysFile(hotkeys)
	}

	val := reflect.ValueOf(hotkeys)

	for i := 0; i < val.NumField(); i++ {
		field := val.Type().Field(i)
		value := val.Field(i)

		if value.Kind() != reflect.Slice || value.Type().Elem().Kind() != reflect.String {
			fmt.Println(lodaHotkeysError(field.Name))
			os.Exit(0)
		}

		hotkeysList := value.Interface().([]string)

		if len(hotkeysList) == 0 || hotkeysList[0] == "" {
			fmt.Println(lodaHotkeysError(field.Name))
			os.Exit(0)
		}
	}

}

// Write hotkeys inside the hotkeys toml file
func writeHotkeysFile(hotkeys HotkeysType) {
	tomlData, err := toml.Marshal(hotkeys)
	if err != nil {
		log.Fatalf("编码热键时出错: %v", err)
	}

	err = os.WriteFile(variable.HotkeysFile, tomlData, 0644)
	if err != nil {
		log.Fatalf("写入热键文件时出错: %v", err)
	}
}

// Load configurations from theme file into &theme and return default values
// if file theme folder is empty
func loadThemeFile() {
	data, err := os.ReadFile(variable.ThemeFolder + "/" + Config.Theme + ".toml")
	if err != nil {
		data = []byte(DefaultThemeString)
	}

	err = toml.Unmarshal(data, &theme)
	if err != nil {
		log.Fatalf("解析主题文件时出错（主题文件可能有误）: %v", err)
	}
}

// Load all default configurations from superfile_config folder into global
// configurations variables
func LoadAllDefaultConfig(content embed.FS) {

	temp, err := content.ReadFile("src/superfile_config/hotkeys.toml")
	if err != nil {
		return
	}
	HotkeysTomlString = string(temp)

	temp, err = content.ReadFile("src/superfile_config/config.toml")
	if err != nil {
		return
	}
	ConfigTomlString = string(temp)

	temp, err = content.ReadFile("src/superfile_config/theme/catppuccin.toml")
	if err != nil {
		return
	}
	DefaultThemeString = string(temp)

	currentThemeVersion, err := os.ReadFile(variable.ThemeFileVersion)

	if err != nil && !os.IsNotExist(err) {
		outPutLog("读取文件时出错:", err)
		return
	}

	_, err = os.Stat(variable.ThemeFolder)

	if os.IsNotExist(err) {
		err := os.MkdirAll(variable.ThemeFolder, 0755)
		if err != nil {
			outPutLog("创建主题目录时出错", err)
			return
		}
	} else if string(currentThemeVersion) == variable.CurrentVersion {
		return
	}

	files, err := content.ReadDir("src/superfile_config/theme")
	if err != nil {
		outPutLog("从嵌入文件读取主题目录时出错", err)
		return
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}
		src, err := content.ReadFile(filepath.Join("src/superfile_config/theme", file.Name()))
		if err != nil {
			outPutLog("从嵌入文件读取主题文件时出错", err)
			return
		}

		file, err := os.Create(filepath.Join(variable.ThemeFolder, file.Name()))
		if err != nil {
			outPutLog("创建主题文件时出错", err)
			return
		}
		file.Write(src)
		defer file.Close()
	}

	os.WriteFile(variable.ThemeFileVersion, []byte(variable.CurrentVersion), 0644)
}
